/// by Alvaro Cuno, Fev2008

#ifndef __FBO_H_
#define __FBO_H_

#include "oglutils.h"
#include <cassert>

/// Defines a Frame Buffer Object parameterized by 
/// the number of texture color objects [1..4]
template<unsigned T = 1>
class FBO {
public:
   FBO() {
      
      NCHANNNELS=4; 
      fboColorTex[0]=GL_COLOR_ATTACHMENT0_EXT;
      fboColorTex[1]=GL_COLOR_ATTACHMENT1_EXT; 
      fboColorTex[2]=GL_COLOR_ATTACHMENT2_EXT;
      fboColorTex[3]=GL_COLOR_ATTACHMENT3_EXT;
   }
   
   FBO(int w, int h) {
      initFBO(w, h);
      NCHANNNELS = 4;
      
      fboColorTex[0]=GL_COLOR_ATTACHMENT0_EXT;
      fboColorTex[1]=GL_COLOR_ATTACHMENT1_EXT; 
      fboColorTex[2]=GL_COLOR_ATTACHMENT2_EXT;
      fboColorTex[3]=GL_COLOR_ATTACHMENT3_EXT;
   }
   
   ~FBO() {
     glDeleteTextures(T, texFBO);
     glDeleteFramebuffersEXT(1, &fboId);
   }
   
   GLuint getTextureId(int i) {
      return texFBO[i];
   }
   
   void setNumberOfChannels(int i) {
      NCHANNNELS = i;
   }
   
   void on() {
      glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);
      setFboView();
   }
   
   void off() {
      glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
      glClearColor(1.0,1.0,1.0,1.0);
      glClear(GL_COLOR_BUFFER_BIT);
   }
   
   /// Creates FrameBuffer Object for offscreen rendering
   void initFBO(int w, int h) {
      
      texSizeW = w; texSizeH = h;
      
      glGenFramebuffersEXT(1, &fboId);
      glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluOrtho2D(0.0, texSizeW, 0.0, texSizeH);
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
      glViewport(0, 0, texSizeW, texSizeH);
      
      // Set the texture object for off-screen rendering
      setColorTextures();
      
      // turn off the fbo
      glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
      glClearColor(1.0,1.0,1.0,1.0);
      glClear(GL_COLOR_BUFFER_BIT);
   }
   
private:
   
   void setFboView() {
      
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluOrtho2D(0.0, texSizeW, 0.0, texSizeH);
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
      glViewport(0, 0, texSizeW, texSizeH);
      
      glClearColor(0.0,0.0,0.0,0.0);
      for (uint i=0; i<T; ++i) {
         glDrawBuffer(fboColorTex[i]);
         glClear(GL_COLOR_BUFFER_BIT);
      }
   }
   
   void setColorTextures() {
      
      assert(T>0 and T<5);
      assert(NCHANNNELS>0 and NCHANNNELS<5);
      
      glGenTextures(T, texFBO);
      for (uint i=0; i<T; ++i) {
         glBindTexture(GL_TEXTURE_RECTANGLE_NV, texFBO[i]);
         glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
         glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
         glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_WRAP_S, GL_CLAMP);
         glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_WRAP_T, GL_CLAMP);
         
         if (NCHANNNELS == 1) 
            glTexImage2D(GL_TEXTURE_RECTANGLE_NV, 0, GL_FLOAT_R32_NV, texSizeW, texSizeH, 
                         0, GL_RED, GL_FLOAT, 0);
         else if (NCHANNNELS == 3) 
            glTexImage2D(GL_TEXTURE_RECTANGLE_NV, 0, GL_FLOAT_RGB32_NV, texSizeW, texSizeH, 
                         0, GL_RGB, GL_FLOAT, 0);
         else if (NCHANNNELS == 4) 
            glTexImage2D(GL_TEXTURE_RECTANGLE_NV, 0, GL_FLOAT_RGBA32_NV, texSizeW, texSizeH, 
                         0, GL_RGBA, GL_FLOAT, 0);
         else {
            cout<<"the number of channels per texture is incorrect!!"<<endl;
            assert(true);
         }
         
         glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, fboColorTex[i], 
                                   GL_TEXTURE_RECTANGLE_NV, texFBO[i], 0);
         
         // clear the buffers
         glDrawBuffer(fboColorTex[i]);
         glClearColor(0.0,0.0,0.0,0.0);
         glClear(GL_COLOR_BUFFER_BIT);
      }
   }
   
private:
   int      texSizeW;
   int      texSizeH;
   GLuint   texFBO[T];   // 
   GLuint   fboId;       // FBO identifier
   int      NCHANNNELS;  // the number of channels (red, green, blue, alpha)
   GLenum   fboColorTex[4]; // the color buffers
};

#endif
